<?php 
/***
    Amrita ITEWS - Copyright (C) 2009 Amrita Vishwa Vidyapeetham, Amritapuri.
                                     (http://www.amrita.edu)
    ***************************************************************************
    This file is part of the Amrita ITEWS distribution.
    Amrita ITEWS is free software; you can redistribute it and/or modify it 
    under the terms of the GNU General Public License (version 2) as published 
    by the Free Software Foundation AND MODIFIED BY the Amrita ITEWS exception.
    ***NOTE*** The exception to the GPL is included to allow you to distribute
    a combined work that includes Amrita ITEWS without being obliged to provide
    the source code for proprietary components outside of the Amrita ITEWS
    software. Amrita ITEWS is distributed in the hope that it will be useful, 
    but WITHOUT ANY WARRANTY; without even the implied warranty of 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
    Public License for more details. You should have received a copy of the GNU
    General Public License and the Amrita ITEWS license exception along with 
    Amrita ITEWS if not then it can be viewed here: 
    http://itews.amrita.ac.in/license.html.
    
    Documentation, latest information, license and contact details are at:
    http://itews.amrita.ac.in/
 
    Amrita ITEWS source code can be found at:
    http://code.google.com/p/itews"; 
    
    The initial code-base of Amrita ITEWS was developed by Amrita Vishwa 
    Vidyapeetham as part of the project titled,\"Intelligent & Interactive 
    Telematics using Emerging Wireless Technologies for Transport Systems 
    (ITEWS)\" sponsored by Technology Information Forecasting and Assessment 
    Council (TIFAC), India.
***/
?><?php
class LocationUpdateController extends AppController {
	
	var $uses = array('TelematicUnit', 'VehicleCurrentNode', 'Trip', 'RouteNodeAssoc', 'Configuration', 'ConfigurationParameter');
	var $components = array('TdcsConfigure'); 
	var $allowCommit = true;
	
	/**
	 * Enter description here...
	 *
	 */
	function beforeFilter() {
		
		// Set Timezone
		date_default_timezone_set("Asia/Calcutta");
		$this->setAutoIncremetsForModelObjects();
		
	}

    /**
     * Enter description here...
     *
     */
	function index() {
    	
		// Start transactions
		$this->VehicleCurrentNode->begin();
		$this->Trip->begin();
		
		// Because CakePHP loads models in a lazy fashion,
		// it's usually not a good idea to create model instances in components.
		// If you need model data in a component,
		// it's best to pass that data in through one of the component's methods:
		$this->TdcsConfigure->setModel($this->ConfigurationParameter);
		
		// Check whether the system is in unit test mode 
		$unitTestMode = $this->TdcsConfigure->isUnitTestMode();
		
		// In Real Time mode service accepts POST data and gets current time of day from system time
		// The message posted to the service has following format: TGATEx,IVTUxx,IVTUxx,..
		// In Unit Test mode do not accepts any POST data, User has to send the update information
		// throgh GET parameter 'test_data' and a test time of day through 'test_time'
		
		if ($unitTestMode) {
			// Getting GET parameters
			if (isset($_GET['test_data']) && isset($_GET['test_time'])) {
				$postMessage = strtolower($_GET['test_data']);
				$testTime    = $_GET['test_time'];
				if (!$this->isValidTime($testTime)) {
					$this->allowCommit = false;
				//	$this->Session->setFlash('Unit Test mode. Please provide a valid test time in hhmm format.', 'message_error');
				//	$this->render('../webservices/index', 'nomenu');
					echo "Unit Test mode. Please provide a valid test time in hhmm format.\n";
					exit(0);	
				} else {
					// Convert $testTime to string format.
					$testTime = (string)($testTime) ;
					// Convert $testTime to date format.
					$currentTime  = date("Y-m-d H:i:s", strtotime($testTime));
				}	
			} else {
				// Service runs under Unit test mode and no GET parameters
				$this->allowCommit = false;
			//	$this->Session->setFlash('Unit Test mode. Please supply GET parameters through URL.', 'message_error');
			//	$this->render('../webservices/index', 'nomenu');		
				echo "Unit Test mode. Please supply GET parameters 'test_data' and 'test_time' through URL.\n";
				exit(0);	
			}	
		} else {
			if (isset($_GET['test_data']) || isset($_GET['test_time'])) {
				// Service runs under Real mode and unwanted GET parameters
				$this->allowCommit = false;
			//	$this->Session->setFlash('Not in Unit Test mode. Please remove any unnecessary GET parameters.', 'message_error');
			//	$this->render('../webservices/index', 'nomenu');		
				echo "Not in Unit Test mode. Please remove any unnecessary GET parameters.\n";
				exit(0);	
			}
			// The POST data has to be read via php://input
			$postMessage = trim(file_get_contents("php://input"));
			$currentTime = date("Y-m-d H:i:s",time());
		}
		
		// Check post data
		/*	
		if(!ereg('[[:alnum:]]\,', $postMessage)){
			$this->Session->setFlash('In valid data.', 'message_error');
			$this->render('../webservices/index', 'nomenu');		
			exit(0);	
		}
		*/
		
		// Process POST data
		$IvtuCodes = explode(",", $postMessage);
		// Now $IvtuCodes[0] is the TGATE code of TU installed at a node 
		// Now $IvtuCodes[1] is the expiry time in UNIX-TIME format 
		// And rest of $IvtuCodes contains TVTU codes of TUs installed in vehicles passing through that node.

		// Before getting TGATE code and Expiry time check whether theier existance in the supplied data.
		if (!array_key_exists(0, $IvtuCodes)) {
			echo "No TGATE code found in supplied data.\n";
			exit(0);
		}
		if (!array_key_exists(1, $IvtuCodes)) {
			echo "No Expiry time found in supplied data.\n";
			exit(0);
		}
		
		// Extract TGATE code and Expiry time
		$TgateCode  = $IvtuCodes[0];
		$expiryTime = $IvtuCodes[1];
		
		unset($IvtuCodes[0]);
		unset($IvtuCodes[1]);
		
		// Check validity of Expiry Time
		if (!$this->isValidUnixTime($expiryTime)) {
			echo "Invalid UNIX-TIME '$expiryTime' in supplied data.\n";
			exit(0);
		}
		
		$currentTime = time(); 
		
		if ($expiryTime < $currentTime) {
			// Update message expired
			// Do nothing
			exit(0);
		}
		
		$currentTime = date("Y:m:d:H:i:s ");

		// check tgate code is valid
		// Translate tgate-code to node-id
		$fields['TelematicUnitType.telematic_unit_type_code'] = "tgate"; 
		$fields['TelematicUnit.telematic_unit_code'] = $TgateCode;
		if ($this->TelematicUnit->hasAny($fields)) {
			$nodeId = $this->TelematicUnit->field('Hotzone.node_id', $fields);
		} else {
			$this->allowCommit = false;
		//	$this->Session->setFlash("Invalid TGATE code '$TgateCode' in supplied data.", 'message_error');
		//	$this->render('../webservices/index', 'nomenu');		
			echo "Invalid TGATE code '$TgateCode' in supplied data.\n";
			exit(0);
		}
		// check ivtu codes are valid
		foreach ($IvtuCodes as $ivtuCode) {
			unset($fields);
			$fields['TelematicUnitType.telematic_unit_type_code'] = "ivtu"; 
			$fields['TelematicUnit.telematic_unit_code'] = trim($ivtuCode);
			if (!$this->TelematicUnit->hasAny($fields)) {
				$this->allowCommit = false;
			//	$this->Session->setFlash("Invalid TelematicUnit code '$ivtuCode' in supplied data.", 'message_error');
			//	$this->render('../webservices/index', 'nomenu');		
				echo "Invalid TelematicUnit code '$ivtuCode' in supplied data.\n";
				exit(0);
			}
		}
		
		// Update location of each vehicle with current time
		foreach ($IvtuCodes as $ivtuCode) {
			$ivtuCode = trim($ivtuCode);
			$fields['TelematicUnitType.telematic_unit_type_code'] = "ivtu"; 
			$fields['TelematicUnit.telematic_unit_code'] = trim($ivtuCode);
			$vehicleId = $this->TelematicUnit->field('TelematicUnit.vehicle_id', $fields);
			$conditions = array('VehicleCurrentNode.vehicle_id'=>$vehicleId, 'VehicleCurrentNode.node_id'=>$nodeId);
			$vehicleCurrentNode = $this->VehicleCurrentNode->find($conditions);
			if (empty($vehicleCurrentNode)) {
				// No vehicle-current-node entry for this vehicle
				// Vehicle reached a new node
				// Insert a new vehicle current node entry for this vehicle.
				unset($data);
				$data['VehicleCurrentNode']['id']                                        = null; 
				$data['VehicleCurrentNode']['vehicle_id']                                = $vehicleId; 
				$data['VehicleCurrentNode']['node_id']                                   = $nodeId;
				$data['VehicleCurrentNode']['vehicle_current_node_arrival_timestamp']    = $currentTime;
				$data['VehicleCurrentNode']['vehicle_current_node_lastupdate_timestamp'] = $currentTime;
				if (!$this->VehicleCurrentNode->save($data)) {
					$this->allowCommit = false;
					//$this->Session->setFlash("Error while updating vehicle current node list.", 'message_error');
					//$this->render('../webservices/index', 'nomenu');		
					echo "Error while updating vehicle current node list.\n";
					exit(0);
				} 
				$tripId = $this->isTripStartNode($vehicleId, $nodeId);
				if ($tripId) {
					//If the current node is same as last trip, set the status to STARTED in trip table
					//Also make an entry in the the tripupdates table
					unset($data);
					$data['Trip']['id'] = $tripId; 
					$data['Trip']['trip_status'] = 'S';	//Trip status can be either I, S, E or C. I=Inactive, S=Started, E=Ended, C= Cancelled.
					$data['Trip']['trip_last_start_time'] = $currentTime;
					if ($this->Trip->save($data, false)) {
						//$this->pushToTripUpdateQueue($tripId);
					} else {
						$this->allowCommit = false;
					//	$this->Session->setFlash("Error while updating trip information.", 'message_error');
					//	$this->render('../webservices/index', 'nomenu');		
						echo "Error while updating trip information.\n";
						exit(0);
					}
				} 
				$tripId = $this->isTripEndNode($vehicleId, $nodeId);
				if ($tripId) {
					//If the current node is same as last trip, set the status to ENDED in trip table
					//Also make an entry in the the tripupdates table
					unset($data);
					$data['Trip']['id'] = $tripId; 
					$data['Trip']['trip_status'] = 'E';	//Trip status can be either I, S, E or C. I=Inactive, S=Started, E=Ended, C= Cancelled.
					$data['Trip']['trip_last_end_time'] = $currentTime;
					if ($this->Trip->save($data, false)) {
						//$this->pushToTripUpdateQueue($tripId);
					} else {
						$this->allowCommit = false;
					//	$this->Session->setFlash("Error while updating trip information.", 'message_error');
					//	$this->render('../webservices/index', 'nomenu');		
						echo "Error while updating trip information.\n";
						exit(0);
					}
				} 
			} else {
				// A vehicle-current-node entry exists; Update vehicle-current-node entry.
				// The vehicle is still at the same node
				// Update lastupdate timestamp.
				$data['VehicleCurrentNode']['id']                                       = $vehicleCurrentNode['VehicleCurrentNode']['id']; 
				$data['VehicleCurrentNode']['vehicle_current_node_lastupdate_timestamp'] = $currentTime; 
				//$this->VehicleCurrentNode->saveField('vehicle_lastupdate_timestamp', $currentTime, false);
				$this->VehicleCurrentNode->save($data, true, array('id', 'vehicle_current_node_lastupdate_timestamp'));
			}
		}
		
		// Since there is no explicit render function call and we are not loggedin user will be redirected to login screen
		// So afterFilter will not be called automatically 
		$this->afterFilter();
		
		// Set Headers
		header("Cache-Control: no-cache");
		header("Pragma: no-cache");
		header("X-TU-List:" . $TgateCode);
		
		// Render output
		//$this->render('../webservices/index', 'webservice');
		
    	if ($this->allowCommit) {
			$this->VehicleCurrentNode->commit();
			$this->Trip->commit();
			exit(1);
		} else {
			$this->VehicleCurrentNode->rollback();
			$this->Trip->rollback();
			exit(0);
		}
		
    }
	
	/**
	 * Enter description here...
	 *
	 * @param unknown_type $vehicleId
	 * @param unknown_type $currentNodeId
	 * @return unknown
	 */
    function isTripEndNode($vehicleId, $currentNodeId) {
		
		// For the current vehicle find out which is the current trip
		// For that trip find the last node i.e. from route_schedule_id, get the route_id, 
		// and from there get the node_id with max sequence number from the route_node_assocs table
		
		// 1. For the current vehicle find out which is the current trip
		$conditions['Trip.vehicle_id'] = $vehicleId; 
		$conditions['Trip.trip_status'] = 'S';	//Trip status can be either I, S, E or C. I=Inactive, S=Started, E=Ended, C= Cancelled. 
		//$this->Trip->recursive = 2;
		$trip = $this->Trip->find($conditions);
		if (empty($trip)) {
			// No active trip for current vehicle.
			//???
			return false; 
		} else {
			// Found a trip
			// 2. For that trip find the last node 
			$routeId    = $trip['RouteSchedule']['route_id'];
			$conditions = array('RouteNodeAssoc.route_id' => $routeId);
			$endNode    = $this->RouteNodeAssoc->find($conditions, 'node_id', 'sequence_no DESC');
			$endNodeId  = $endNode['RouteNodeAssoc']['node_id']; 
			if ($endNodeId == $currentNodeId) {
				// Current node is the last node of the active trip for the vehicle 
				// Return the trip id
				return $trip['Trip']['id']; 
			} else {
				// Current node is not the last node of the active trip for the vehicle 
				return false; 
			}
		}
		
	}
	
	/**
	 * Enter description here...
	 *
	 * @param unknown_type $vehicleId
	 * @param unknown_type $currentNodeId
	 * @return unknown
	 */
	function isTripStartNode($vehicleId, $currentNodeId) {
		
		// For the current vehicle find out which is the current trip
		// For that trip find the last node i.e. from route_schedule_id, get the route_id, 
		// and from there get the node_id with max sequence number from the route_node_assocs table
		
		// 1. For the current vehicle find out which is the current trip
		$conditions['Trip.vehicle_id'] = $vehicleId; 
		//$conditions['Trip.trip_status'] = 'S';	//Trip status can be either I, S, E or C. I=Inactive, S=Started, E=Ended, C= Cancelled. 
		//$this->Trip->recursive = 2;
		$trip = $this->Trip->find($conditions);
		if (empty($trip)) {
			// No active trip for current vehicle.
			//???
			return false; 
		} else {
			// Found a trip
			// 2. For that trip find the last node 
			$routeId    = $trip['RouteSchedule']['route_id'];
			$conditions = array('RouteNodeAssoc.route_id' => $routeId);
			$startNode    = $this->RouteNodeAssoc->find($conditions, 'node_id', 'sequence_no ASC');
			$startNodeId  = $startNode['RouteNodeAssoc']['node_id']; 
			if ($startNodeId == $currentNodeId) {
				// Current node is the last node of the active trip for the vehicle 
				// Return the trip id
				return $trip['Trip']['id']; 
			} else {
				// Current node is not the last node of the active trip for the vehicle 
				return false; 
			}
		}
		
	}
	
	/**
	 * Enter description here...
	 *
	 * @param unknown_type $tripId
	 */
	function pushToTripUpdateQueue($tripId) {
		
		$field['tripupdate_reference'] = $tripId;
		if (!$this->Tripupdate->hasAny($field)) {
			$data['Tripupdate']['tripupdate_type']      = 'trip';
			$data['Tripupdate']['tripupdate_reference'] = $tripId;
			if (!$this->Tripupdate->save($data)) {
				$this->allowCommit = false;
			//	$this->Session->setFlash("Error while updating trip updates queue.", 'message_error');
			//	$this->render('../webservices/index', 'nomenu');
				echo "Error while updating trip updates queue.\n";		
				exit(0);
			}
		}
		
	}
	
	
	/**
	 * Enter description here...
	 *
	 * @param unknown_type $time
	 * @return unknown
	 */
	function isValidTime($time) {
		
		if (ereg('[^0-9]', $time) || strlen($time) != 4) {
			return false;
		} else {
			$hh = (int) ($time / 100);
			$mm = $time - $hh * 100;
			if ($hh >= 24 || $hh < 0 || $mm >= 60 || $mm < 0 ) {
				return false;
			}  
		}
		return true;
		
	}
	
	/**
	 * Enter description here...
	 *
	 * @param unknown_type $unixTime
	 * @return unknown
	 */
	
	
	function isValidUnixTime($unixTime) {
		
		if (ereg('[^0-9]', $unixTime)) {
			return false;
		} else {
			return true;
		}
		
	}
	
	/**
	 * Enter description here...
	 *
	 */
	function beforeRender() {
		
		if ($this->allowCommit) {
			$this->VehicleCurrentNode->commit();
			$this->Trip->commit();
		} else {
			$this->VehicleCurrentNode->rollback();
			$this->Trip->rollback();
		}
		
	}
	
}
?>